{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [深入理解this对象](https://blog.csdn.net/woshinannan741/article/details/51146889)\n",
    "\n",
    "面向对象语言中 this 表示当前对象的一个引用。\n",
    "\n",
    "但在 JavaScript 中 this 不是固定不变的，它会随着执行环境的改变而改变。\n",
    "\n",
    "1. 在方法中，this 表示该方法所属的对象。\n",
    "2. 如果单独使用，this 表示全局对象。\n",
    "3. 在函数中，this 表示全局对象。\n",
    "4. 在函数中，在严格模式下，this 是未定义的(undefined)。\n",
    "5. 在事件中，this 表示接收事件的元素。\n",
    "6. 类似 call() 和 apply() 方法可以将 this 引用到任何对象。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## this的定义\n",
    "在JavaScript中this表示函数运行的时候自动生成的一个内部对象，只能在函数内部使用，总结一句话就是`谁调用的方法this就是指向谁`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true\n"
     ]
    }
   ],
   "source": [
    "function test(){\n",
    "    console.log(this == global);  //this == window，这里是node环境\n",
    "}\n",
    "test();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数调用(指向window)\n",
    "在我们平时定义一个函数的时候然后在调用它，就是指向的`window`对象,就像上面先定义了一个函数`test`,然后去调用它，其实真正调用test方法的是window对象(在JavaScript中都是`对象.属性/方法`，如果是window对象可以省略)，这时候的this是指向window可以用下面的代码进行尝试:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "anlzou\n",
      "undefined\n",
      "undefined\n"
     ]
    }
   ],
   "source": [
    "var a = \"anlzou\"\n",
    "function test(){\n",
    "    console.log(this.a);\n",
    "}\n",
    "test();\n",
    "\n",
    "var t = new test();\n",
    "console.log(t.a);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 作为构造函数调用\n",
    "这个是指向的新的对象,其实这个例子在上面已经提出来了，下面只需改动上面的一个地方也就是在test的函数中加上`this.a=a`,输出的结果就不同了，看下面的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "anlzou\n",
      "anlzou\n",
      "anlzou\n"
     ]
    }
   ],
   "source": [
    "var a = \"anlzou\"\n",
    "function test(){\n",
    "    this.a = a;\n",
    "    console.log(this.a);\n",
    "}\n",
    "test();\n",
    "\n",
    "var t = new test();\n",
    "console.log(t.a);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 作为对象的方法调用\n",
    "函数还可以作为某个对象的方法调用，这时候this就是这个上级的对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "1\n",
      "undefined\n"
     ]
    }
   ],
   "source": [
    "function test(){\n",
    "    console.log(this.x);\n",
    "}\n",
    "var o = {};\n",
    "o.m=test;\n",
    "o.x=1;  // output:1\n",
    "o.m();\n",
    "\n",
    "console.log(o.x);\n",
    "console.log(o.m());"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 作为元素的节点\n",
    "这次先看代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "/*\n",
    "var value = 'nihao';\n",
    "function f1(){\n",
    "    console.log(this.value);\n",
    "}\n",
    "var val = document.getElementById('email');\n",
    "val.onclick=f1;\n",
    "*/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面是一行的HTML代码：\n",
    "\n",
    "`<input id=\"email\" type=\"text\" />`\n",
    "\n",
    "在点击input之后我们会看到弹出的是我们输入的值，而不是window对象的’nihao’，这是 因为每一个获取的DOM元素也是一个对象；"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## call()和apply()\n",
    "这两个函数的作用是相同的语法有一点不同；\n",
    "他的作用是改变函数的调用对象，他的第一个参数就表示改变后的调用这个函数的对象(就是改变使用这个函数对象的this指向)\n",
    "看下面的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gou---shangsha---32\n",
      "anlzou\n",
      "hei---beijing---21\n",
      "anlzou\n"
     ]
    }
   ],
   "source": [
    "var name=\"helios\";\n",
    "function setName(addr,weight){\n",
    "    console.log(this.name+'---'+addr+'---'+weight);\n",
    "    console.log('anlzou');\n",
    "}\n",
    "var cat = {name:'hei',age:12},\n",
    "    dog = {name:'gou'};\n",
    "\n",
    "setName.call(dog,'shangsha',32);\n",
    "setName.apply(cat,['beijing',21]);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "call()方法第一个参数传递的是调用这个方法的对象的this指向(上面的例子就是setName的this指向dog)，后面还可以有多个参数分别是实际参数\n",
    "apply()一共能有两个参数第一个参数和call方法是一样的，第一个参数是实际参数的数组"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 非预期效果\n",
    "this的主要应用前面已经介绍完了，但是在使用`this`的时候，初学者很容易使用this不当，造成非预期效果下面就来说明一下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "undefined\n"
     ]
    }
   ],
   "source": [
    "var obj={\n",
    "    bar:\"yuansu\",\n",
    "    foo:function(){\n",
    "        (function(){\n",
    "            console.log(this.bar);//这个this指向的window对象 会弹出undefined；\n",
    "        })()\n",
    "    }\n",
    "}\n",
    "obj.foo();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里先要指出一个误区，不是在函数里面定义的函数，this就是指向的上一级函数的this这是不正确的，在函数里面定义的函数也是要看是谁调用的这个函数(方法)this才是谁；在这里并不是obj调用foo里面的匿名函数，实际上是`window`,如果想要达到预期的效果值许改一点代码就可以了，看下面代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "anlzou\n"
     ]
    }
   ],
   "source": [
    "var obj={\n",
    "    name:\"anlzou\",\n",
    "    foo:function(){\n",
    "        var self = this;  //把obj这个对象用self保存下来\n",
    "        (function(){\n",
    "            console.log(self.name);//这里的self就是obj了\n",
    "        })()\n",
    "    }\n",
    "}\n",
    "obj.foo();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结\n",
    "|调用形式   |  this指向 |\n",
    "|:-----------|:-----------|\n",
    "|普通函数   |window  |\n",
    "|构造函数   |实例化后的对象|\n",
    "|对象的方法  |该对象|\n",
    "|DOM节点    |该节点对象|\n",
    "|call或者apply|第一个参数|"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Javascript (Node.js)",
   "language": "javascript",
   "name": "javascript"
  },
  "language_info": {
   "file_extension": ".js",
   "mimetype": "application/javascript",
   "name": "javascript",
   "version": "13.13.0"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "259.95px"
   },
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
